/********************************************************************************
 * @file    app_energy_storage.c
 * @brief   耗电量存储应用代码
 * 
 * @author  nzttj
 * @date    2025-03-23
 * @version 1.0
 * 
 * Copyright (C) 2025 nzttj
 * 
 * 本软件使用 GNU 通用公共许可证 (GPL) 第 3 版授权。
 * 
 * 您可以自由使用、修改、分发本软件，但必须遵守以下条件：
 * 1. 源代码必须开放，且您修改后的代码也必须以 GPL 许可证发布。
 * 2. 发布或分发本软件时，必须附带本许可证，并提供源代码或获取源代码的方式。
 * 
 * 本软件按“原样”提供，不附带任何担保，使用风险由用户自行承担。
 * 
 ********************************************************************************/

#include "app_energy_storage.h"

#include "esp_spiffs.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include <string.h>
#include <sys/stat.h>
#include <math.h>

/* 文件路径配置 */
#define STORAGE_FILE_PATH "/spiffs/energy.dat"
#define NVS_NAMESPACE "energy_storage"
#define NVS_WRITE_INDEX_KEY "write_idx"

#define ENERGY_STORAGE_SAVE_INTERVAL    (3600)  // 3600秒保存一次
#define ENERGY_STORAGE_SAVE_THRESHOLD   (0.003) // 0.003KWh（度）保存一次

#define ENERGY_KWH_TO_WH_FACTOR         (1000.0f)
#define ENERGY_X10_TO_KWH_FACTOR        (10.0f * 1000.0f)   // 转换说明：x10存储格式（0.1Wh精度）到KWh

/* 存储上下文结构体 */
typedef struct
{
    nvs_handle_t nvs_handle; // NVS句柄
    uint16_t write_index;    // 当前写入位置
    uint16_t record_count;   // 有效记录数
    bool initialized;        // 初始化标志
} storage_context_t;

static storage_context_t s_ctx = {0};
static energy_daily_record_t s_daily_record = {0};  // 最后一条耗电量信息记录
static const char *TAG = "energy_storage";

/* 内部函数声明 */
static storage_err_t validate_date(const struct tm *tm);
static storage_err_t write_record(uint16_t index, const energy_daily_record_t *record);
static storage_err_t update_write_index(void);
static storage_err_t rotate_index(void);

void app_energy_storage_process(electrical_data_t *data)
{
    typedef struct {
        time_t last_write_time;     // 上一次保存时间
        float kwh_today;            // 今日用电量 单位：KWh（度）
        float last_saved_kwh;       // 上一次保存的电能 单位：KWh（度）
        float kwh_before_today;     // 上电以来到今日0点的总电能 单位：KWh（度）
        float last_saved_today_kwh; // 掉电前保存的今日电能 单位：KWh（度）
        bool is_first_power_on;     // 是否是第一次上电
    } energy_state_t;

    static energy_state_t s_state = {
        .last_write_time = 0,
        .kwh_today = 0.0f,
        .last_saved_kwh = 0.0f,
        .kwh_before_today = 0.0f,
        .last_saved_today_kwh = 0.0f,
        .is_first_power_on = true
    };
    storage_err_t err = STORAGE_OK;

    time_t now = time(NULL);
    struct tm tm;
    localtime_r(&now, &tm);
    err = validate_date(&tm);
    if (err != STORAGE_OK)
    {
        ESP_LOGE(TAG, "Invalid date, err: %d", err);
        return;
    }

    // 检查是否是新的一天
    bool is_new_day = (tm.tm_mday != s_daily_record.day ||
                       (tm.tm_mon + 1) != s_daily_record.month ||
                       (tm.tm_year + 1900) != (s_daily_record.year + 2000));

    if (is_new_day)
    {
        s_state.is_first_power_on = false;
        s_state.kwh_before_today = data->kwh_total;
        s_state.kwh_today = 0.0f;
        s_state.last_saved_kwh = 0.0f;
        s_state.last_saved_today_kwh = 0.0f;

        ESP_LOGI(TAG, "New day init save");
        s_state.last_write_time = now;
        err = app_energy_storage_save_daily_energy(&tm, 0.0f);
        if (err != STORAGE_OK)
        {
            ESP_LOGE(TAG, "New day init save failed, err: %d", err);
        }
        return;
    }
    else if (s_state.is_first_power_on)
    {
        s_state.last_saved_today_kwh = (float)s_daily_record.energy_x10 / ENERGY_X10_TO_KWH_FACTOR;
        s_state.is_first_power_on = false;
        ESP_LOGI(TAG, "First power on");
    }

    // 计算今日用电量
    s_state.kwh_today = data->kwh_total - s_state.kwh_before_today + s_state.last_saved_today_kwh;

    // 检查是否需要保存数据
    bool should_save = (now - s_state.last_write_time >= ENERGY_STORAGE_SAVE_INTERVAL) ||
                       (fabsf(s_state.kwh_today - s_state.last_saved_kwh) >= (ENERGY_STORAGE_SAVE_THRESHOLD - 1e-6));

    if (should_save)
    {
        s_state.last_write_time = now;
        s_state.last_saved_kwh = s_state.kwh_today;
        err = app_energy_storage_save_daily_energy(&tm, s_state.kwh_today * ENERGY_KWH_TO_WH_FACTOR);
        if (err != STORAGE_OK)
        {
            ESP_LOGE(TAG, "Save daily energy failed, err: %d", err);
        }
    }
}

/* 初始化存储系统 */
storage_err_t app_energy_storage_init(void)
{
    if (s_ctx.initialized)
        return STORAGE_OK;

    // 打开NVS命名空间
    if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &s_ctx.nvs_handle) != ESP_OK)
    {
        return STORAGE_NVS_ERROR;
    }

    // 从NVS读取写入位置
    if (nvs_get_u16(s_ctx.nvs_handle, NVS_WRITE_INDEX_KEY, &s_ctx.write_index) != ESP_OK)
    {
        s_ctx.write_index = 0;
    }
    ESP_LOGI(TAG, "Write index: %u", s_ctx.write_index);

    // 初始化SPIFFS
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = true
    };
    if (esp_vfs_spiffs_register(&conf) != ESP_OK)
    {
        return STORAGE_FILE_ERROR;
    }

    // 验证存储文件
    struct stat st = {0};
    if (stat(STORAGE_FILE_PATH, &st) != 0)
    {
        // 创建初始文件
        FILE *fp = fopen(STORAGE_FILE_PATH, "wb");
        if (!fp)
            return STORAGE_FILE_ERROR;
        fclose(fp);
    }

    // 计算有效记录数
    s_ctx.record_count = (st.st_size / sizeof(energy_daily_record_t));
    s_ctx.record_count = (s_ctx.record_count > MAX_DAILY_RECORDS) ? MAX_DAILY_RECORDS : s_ctx.record_count;
    ESP_LOGI(TAG, "Energy storage initialized with %u records", s_ctx.record_count);

    // 读取最后一条记录
    if (s_ctx.record_count > 0)
    {
        if (app_energy_storage_read_record(s_ctx.write_index, &s_daily_record) == STORAGE_OK)
        {
            ESP_LOGI(TAG, "Last record: %u-%u-%u, energy: %u/10 Wh", s_daily_record.year, s_daily_record.month, s_daily_record.day, s_daily_record.energy_x10);
        }
    }

    s_ctx.initialized = true;
    return STORAGE_OK;
}

/* 保存当日能耗数据 */
storage_err_t app_energy_storage_save_daily_energy(struct tm *tm, float energy_wh)
{
    storage_err_t err = STORAGE_OK;
    // 准备记录数据
    energy_daily_record_t record = {
        .year = (uint16_t)(tm->tm_year + 1900 - 2000),
        .month = (uint8_t)(tm->tm_mon + 1),
        .day = (uint8_t)tm->tm_mday,
        .energy_x10 = (uint32_t)(energy_wh * 10)
    };

    // 写入存储系统
    ESP_LOGI(TAG, "Save daily energy, record: %u-%u-%u, energy: %u/10 Wh", record.year, record.month, record.day, record.energy_x10);

    // 如果日期发生变化
    bool is_new_day = (s_daily_record.year != record.year ||
                       s_daily_record.month != record.month ||
                       s_daily_record.day != record.day);
    if (is_new_day)
    {
        // 如果之前不存在记录，则等待第二天再更新索引，即当天的记录是没有更新索引的
        if (s_ctx.record_count == 0)
        {
            // 第一条记录
            s_ctx.record_count = 1;
        }
        else
        {
            // 如果存在记录，则更新索引
            if (rotate_index() != STORAGE_OK)
                return STORAGE_NVS_ERROR;
        }

        ESP_LOGI(TAG, "write record index: %u", s_ctx.write_index);
        err = write_record(s_ctx.write_index, &record);
        if (err != STORAGE_OK)
            return err;
    }
    else
    {
        ESP_LOGI(TAG, "write record index: %u", s_ctx.write_index);
        err = write_record(s_ctx.write_index, &record);
        if (err != STORAGE_OK)
            return err;
    }

    memcpy(&s_daily_record, &record, sizeof(energy_daily_record_t));

    return STORAGE_OK;
}

/* 获取指定日期能耗 */
storage_err_t app_energy_storage_get_energy(time_t date, float *energy_wh)
{
    struct tm *tm = localtime(&date);
    if (validate_date(tm) != STORAGE_OK)
        return STORAGE_INVALID_DATE;

    energy_daily_record_t target = {
        .year = (uint16_t)(tm->tm_year + 1900 - 2000),
        .month = (uint8_t)(tm->tm_mon + 1),
        .day = (uint8_t)tm->tm_mday
    };

    // 线性搜索（数据量小无需二分）
    ESP_LOGI(TAG, "record count: %u", s_ctx.record_count);
    for (uint16_t i = 0; i < s_ctx.record_count; ++i)
    {
        // ESP_LOGI(TAG, "Search energy, index: %u", i);
        energy_daily_record_t record;
        storage_err_t ret = app_energy_storage_read_record(i, &record);
        if (ret != STORAGE_OK)
        {
            ESP_LOGE(TAG, "Read record failed, index: %u, ret: %d", i, ret);
            continue;
        }

        if (memcmp(&record, &target, 4) == 0)
        { // 比较年月日
            *energy_wh = record.energy_x10 * ENERGY_UNIT;
            return STORAGE_OK;
        }

        // ESP_LOGI(TAG, "year: %u, month: %u, day: %u, energy: %u", record.year, record.month, record.day, record.energy_x10);
    }
    return STORAGE_DATA_CORRUPTED;
}

/* 获取所有记录的API实现 */
storage_err_t app_energy_storage_get_all_records(energy_daily_record_t *buffer, uint16_t *count)
{
    if (!buffer || !count)
        return STORAGE_INVALID_DATE;

    uint16_t records_to_read = (s_ctx.record_count < MAX_DAILY_RECORDS) ? s_ctx.record_count : MAX_DAILY_RECORDS;

    for (uint16_t i = 0; i < records_to_read; ++i)
    {
        storage_err_t ret = app_energy_storage_read_record(i, &buffer[i]);
        if (ret != STORAGE_OK)
        {
            *count = i; // 返回已成功读取的数量
            return ret;
        }
    }

    *count = records_to_read;
    return STORAGE_OK;
}

/* 获取数据条数 */
storage_err_t app_energy_storage_get_record_count(uint16_t *cnt)
{
    *cnt = s_ctx.record_count;
    return STORAGE_OK;
}

/* 内部工具函数 */
static storage_err_t validate_date(const struct tm *tm)
{
    if (tm->tm_year < (2024 - 1900) || tm->tm_year > (2255 - 1900))
    {
        return STORAGE_INVALID_DATE;
    }
    if (tm->tm_mon < 0 || tm->tm_mon >= 12)
        return STORAGE_INVALID_DATE;

    // 精确日期校验（考虑每月天数）
    int max_day = 31;
    switch (tm->tm_mon + 1)
    {
    case 4:
    case 6:
    case 9:
    case 11:
        max_day = 30;
        break;
    case 2:
        max_day = ((tm->tm_year + 1900) % 4 == 0) ? 29 : 28;
        break;
    }
    return (tm->tm_mday >= 1 && tm->tm_mday <= max_day) ? STORAGE_OK : STORAGE_INVALID_DATE;
}

static storage_err_t write_record(uint16_t index, const energy_daily_record_t *record)
{
    FILE *fp = fopen(STORAGE_FILE_PATH, "rb+");
    if (!fp)
        return STORAGE_FILE_ERROR;

    // 计算写入位置
    const long offset = (index % MAX_DAILY_RECORDS) * sizeof(energy_daily_record_t);
    ESP_LOGI(TAG, "write record offset: %lu", offset);
    if (fseek(fp, offset, SEEK_SET) != 0)
    {
        fclose(fp);
        return STORAGE_FILE_ERROR;
    }

    // 执行写入
    size_t written = fwrite(record, sizeof(energy_daily_record_t), 1, fp);
    fclose(fp);
    return (written == 1) ? STORAGE_OK : STORAGE_FILE_ERROR;
}

storage_err_t app_energy_storage_read_record(uint16_t index, energy_daily_record_t *record)
{
    if (index >= s_ctx.record_count)
    {
        return STORAGE_INVALID_DATE; // 实际应为无效索引错误，可扩展错误码
    }

    FILE *fp = fopen(STORAGE_FILE_PATH, "rb");
    if (!fp)
    {
        return STORAGE_FILE_ERROR;
    }

    // 计算物理存储位置（循环缓冲区模式）
    const uint16_t physical_idx = (s_ctx.write_index - (s_ctx.record_count - 1) + index + MAX_DAILY_RECORDS) % MAX_DAILY_RECORDS;
    const long offset = physical_idx * sizeof(energy_daily_record_t);
    storage_err_t ret = STORAGE_OK;
    if (fseek(fp, offset, SEEK_SET) != 0)
    {
        ret = STORAGE_FILE_ERROR;
        goto cleanup;
    }

    size_t read = fread(record, sizeof(energy_daily_record_t), 1, fp);
    if (read != 1)
    {
        ret = STORAGE_DATA_CORRUPTED;
    }

    // 基础数据校验
    if (ret == STORAGE_OK)
    {
        if (record->month == 0 || record->month > 12 ||
            record->day == 0 || record->day > 31)
        {
            ret = STORAGE_DATA_CORRUPTED;
        }
    }

cleanup:
    fclose(fp);
    return ret;
}

static storage_err_t rotate_index(void)
{
    s_ctx.write_index = (s_ctx.write_index + 1) % MAX_DAILY_RECORDS;

    // 更新有效记录数
    if (s_ctx.record_count < MAX_DAILY_RECORDS)
    {
        s_ctx.record_count++;
    }

    // 持久化存储索引
    return update_write_index();
}

static storage_err_t update_write_index(void)
{
    if (nvs_set_u16(s_ctx.nvs_handle, NVS_WRITE_INDEX_KEY, s_ctx.write_index) != ESP_OK)
    {
        return STORAGE_NVS_ERROR;
    }
    return nvs_commit(s_ctx.nvs_handle) == ESP_OK ? STORAGE_OK : STORAGE_NVS_ERROR;
}

// 增加一条指令将所有耗电量数据删除
storage_err_t app_energy_storage_delete_all_records(void)
{
    // 清空文件内容
    FILE *fp = fopen(STORAGE_FILE_PATH, "wb");
    if (!fp)
    {
        return STORAGE_FILE_ERROR;
    }
    fclose(fp);

    // 重置写入索引
    s_ctx.write_index = 0;
    s_ctx.record_count = 0;

    // 清空最后一条记录缓存
    memset(&s_daily_record, 0, sizeof(energy_daily_record_t));

    // 更新NVS中的写入索引
    storage_err_t err = update_write_index();
    if (err != STORAGE_OK)
    {
        ESP_LOGE(TAG, "Failed to update write index in NVS");
        return err;
    }

    ESP_LOGI(TAG, "All energy records deleted successfully");
    return STORAGE_OK;
}
